﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using ArticleWeb.Models;
using Newtonsoft.Json.Linq;
using Microsoft.AspNet.Identity;
using System.Linq.Expressions;

namespace ArticleWeb.Controllers
{
    [Authorize]
    public class ArticlesController : Controller
    {
        private ApplicationDbContext db = new ApplicationDbContext();

        // GET: Articles
        public ActionResult Index(string kw, int? catId1, int? catId2, int? catId3, ArticleState? state, string time, int page = 1, int size = 15, bool excludeKw = false, bool delList = false)
        {
            List<int> catIds = new List<int>();
            if (catId1 != null && catId2 == null)
            {
                var catId2List = _GetSubCategories(catId1).Select(x => x.ID);
                foreach (var item in catId2List)
                {
                    catIds.AddRange(_GetSubCategories(item).Select(x => x.ID));
                }
            }
            if (catId2 != null && catId3 == null)
            {
                catIds.AddRange(_GetSubCategories(catId2).Select(x => x.ID));
            }

            if (catId3 != null)
            {
                catIds.Add(catId3.Value);
            }
            var timerange = TimeUitl.GetTimeRange(time);
            ViewBag.AllCategories = db.ArticleCategories.Select(x => new IDName { ID = x.ID, Name = x.Name }).ToList();
            ViewBag.Categories = _GetSubCategories(null);
            var query = db.Articles
                .Where(x => !string.IsNullOrEmpty(kw) ? (excludeKw ? !x.Title.Contains(kw) : x.Title.Contains(kw)) : true)
                .Where(x => !catIds.Any() ? true : catIds.Contains(x.CatagoryID))
                .Equal(x => x.State, state)
                .Between(x => x.CreateTime, timerange.TimeFrom, timerange.TimeTo)
                .OrderBy(x => x.ID);
            ViewBag.Total = query.Count();
            ViewBag.Categories2 = catId1.HasValue ? _GetSubCategories(catId1.Value) : new List<IDName>();
            ViewBag.Categories3 = catId2.HasValue ? _GetSubCategories(catId2.Value) : new List<IDName>();
            ViewBag.Kw = kw;
            ViewBag.ExcludeKw = excludeKw;
            ViewBag.Page = page;
            ViewBag.Size = size;
            var list = query.ToList(page, size);
            if (delList)
            {
                foreach (var item in list)
                {
                    db.Articles.Remove(item);
                }
                db.SaveChanges();
                list.Clear();
            }
            return View(list);
        }

        private List<IDName> _GetSubCategories(int? catId)
        {
            return db.ArticleCategories.Where(x => x.ParentID == catId).Select(x => new IDName { ID = x.ID, Name = x.Name }).ToList();
        }

        public ActionResult GetSubCategories(int catId)
        {
            return this.JsonOk(_GetSubCategories(catId));
        }

        [HttpPost]
        public ActionResult Merge(string ids)
        {
            var idArray = Newtonsoft.Json.JsonConvert.DeserializeObject<int[]>(ids);
            if (idArray.Length < 2)
            {
                return this.JsonError("要合并文章，至少要选择两篇文章");
            }
            else
            {
                var list = db.Articles.Where(x => idArray.Contains(x.ID)).Select(x => new
                {
                    x.ID,
                    x.Content.Length
                }).ToList();
                var len = list.Max(x => x.Length);
                var item = list.Find(x => x.Length == len);
                var targets = list.Where(x => x.ID != item.ID).Select(x => x.ID).ToList();
                var rowsAffected = db.DeleteByIdList("Articles", targets);
                var rowsAffected2 = db.DeleteByTwoFieldValues("ArticleSimilarities", "LAID", "RAID", targets);
                return this.JsonOk();
            }
        }

        public ActionResult Similarity(int page = 1, int s = 85)
        {
            ViewBag.pending = db.Articles.Count(x => x.SimHash == null);
            var list = db.ArticleSimilarities
                .Where(x => x.Value > s)
                .GroupBy(x => x.LAID)
                .Select(x => new
                {
                    ID = x.Key, /*LAID*/
                    Count = x.Count(),
                    //Ids = x.Select(item => item.RAID)
                }).OrderByDescending(x => x.Count);
            var cmd = db.Database.Connection.CreateCommand();
            cmd.CommandText = "select count(num) from (SELECT COUNT(LAID) as num FROM ArticleSimilarities WHERE VALUE>@s GROUP BY LAID) x";
            cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("s", s));
            db.Database.Connection.Open();
            ViewBag.Total = (int)cmd.ExecuteScalar();
            db.Database.Connection.Close();
            var groupList = list.ToList(page);
            var ids = groupList.Select(x => x.ID);
            var articles = db.Articles.Where(x => ids.Contains(x.ID)).Select(x => new { x.ID, x.Title, x.Fragments, x.CreateTime }).ToList();//以上500ms
            var query = groupList.Join(articles, x => x.ID /*LAID*/, x => x.ID, (l, r) => new SimilarityResult
            {
                ID = l.ID,
                Title = r.Title,
                Count = l.Count,
                Fragments = r.Fragments,
                CreateTime = r.CreateTime
            });
            return View(query);//以上1ms
        }

        public ActionResult SimilarList(int? id, int page = 1, int s = 70)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            var idQuery = db.ArticleSimilarities
                .OrderBy(x => x.Value)
                .Where(x => x.LAID == id && x.Value > s).Select(x => x.RAID);
            var list = idQuery.ToList(page);
            list.Add(id.Value);
            list = list.Distinct().ToList();
            ViewBag.Total = idQuery.Count();
            var query = db.Articles.Where(x => list.Contains(x.ID));
            return View(query);
        }

        // GET: Articles/Details/5
        public ActionResult Details(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Article article = db.Articles.Find(id);
            if (article == null)
            {
                return HttpNotFound();
            }
            return View(article);
        }

        // GET: Articles/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: Articles/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateInput(false)]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "Title,Content,CatagoryID")] Article article)
        {
            if (ModelState.IsValid)
            {
                var simHash = SimHashAnalyser.GetSimHash(article.Content);
                var ids = db.Articles.Select(x => new { ID = x.ID, x.SimHash }).ToList();
                article.CatagoryID = 2;
                article.CreateTime = DateTime.Now;
                article.State = ArticleState.待采编;
                article.SimHash = simHash;
                db.Articles.Add(article);
                db.SaveChanges();
                foreach (var item in ids)
                {
                    int value = (int)(SimHashAnalyser.GetLikenessValue(simHash, item.SimHash.Value) * 100);
                    if (value > 70)
                    {
                        db.ArticleSimilarities.Add(new ArticleSimilarity
                        {
                            CategoryID = article.CatagoryID,
                            LAID = article.ID,
                            RAID = item.ID,
                            Value = value
                        });
                        db.ArticleSimilarities.Add(new ArticleSimilarity
                        {
                            CategoryID = article.CatagoryID,
                            LAID = item.ID,
                            RAID = article.ID,
                            Value = value
                        });
                    }
                }
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(article);
        }

        // GET: Articles/Edit/5
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Article article = db.Articles.Find(id);
            if (article == null)
            {
                return HttpNotFound();
            }
            return View(article);
        }

        public ActionResult Edit2(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Article article = db.Articles.Find(id);
            if (article == null)
            {
                return HttpNotFound();
            }
            if (article.State == ArticleState.已采编)
            {
                return View("_ArticleLockProcessed");
            }
            var locked = (article.EditingBy != null && article.EditingBy != Guid.Parse(User.Identity.GetUserId()));
            if (!locked)
            {
                article.EditingBy = Guid.Parse(User.Identity.GetUserId());
                db.Entry(article).State = EntityState.Modified;
                db.SaveChanges();
                return View(article);
            }
            else
            {
                return View("_ArticleLock");
            }
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
        [ValidateInput(false)]
        public ActionResult Edit2(int? id, string json)
        {
            if (id == null)
                return this.JsonBadRequest();
            var userId = Guid.Parse(User.Identity.GetUserId());
            var items = JArray.Parse(json);
            var m = db.Articles.Find(id);
            if (m == null)
            {
                return this.JsonNotFound();
            }
            if (m.State == ArticleState.已采编)
            {
                return this.JsonError("操作已终止，因为这篇文章已经采编过了。");
            }
            foreach (JToken item in items)
            {
                var tagText = item.Value<string>("tag");
                var content = item.Value<string>("content");
                var tag = db.ArticleTags.FirstOrDefault(x => x.TagText == tagText);
                if (tag == null)
                {
                    tag = new ArticleTag { CreateTime = DateTime.Now, TagText = tagText, UpdateTime = DateTime.Now, Fragments = 0 };
                    db.ArticleTags.Add(tag);
                    db.SaveChanges();
                }
                tag.Fragments++;
                db.Entry(tag).State = EntityState.Modified;
                db.SaveChanges();
                db.ArticleFragments.Add(new ArticleFragment
                {
                    ArticleID = id.Value,
                    EditorID = userId,
                    CategoryID = m.CatagoryID,
                    Content = content,
                    TagID = tag.ID,
                    Length = content.Length,
                    AuditState = ArticleAuditState.未审核,
                    UpdateTime = DateTime.Now
                });
                m.Fragments++;
                db.SaveChanges();
            }
            m.EditingBy = null;
            m.State = ArticleState.已采编;
            db.Entry(m).State = EntityState.Modified;
            db.SaveChanges();
            return this.JsonOk();
        }

        // POST: Articles/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        [ValidateInput(false)]
        public ActionResult Edit([Bind(Include = "ID,Title,Content,Fragments,CatagoryID,CreateTime,EditingBy,State")] Article article)
        {
            if (ModelState.IsValid)
            {
                var m = db.Articles.Find(article.ID);
                m.Title = article.Title;
                m.Content = article.Content;
                db.Entry(m).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(article);
        }

        // GET: Articles/Delete/5
        public ActionResult Delete(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Article article = db.Articles.Find(id);
            if (article == null)
            {
                return HttpNotFound();
            }
            if (Request.IsAjaxRequest())
            {
                return this.JsonToken();
            }
            return View(article);
        }
        [AllowAnonymous]
        [HttpPost]
        [ValidateInput(false)]
        public ActionResult Import2(int catId, string title, string content, string srcUrl)
        {
            var article = new Article();
            article.CatagoryID = catId;
            article.Title = title;
            article.Content = content;
            article.SourceUrl = srcUrl;
            article.CreateTime = DateTime.Now;
            db.Articles.Add(article);
            db.SaveChanges();
            return this.JsonEx(200, "OK");
        }
        [AllowAnonymous]
        [HttpPost]
        [ValidateInput(false)]
        public ActionResult Import(string catName, string title, string content, string srcUrl)
        {
            var cat = db.ArticleCategories.FirstOrDefault(x => x.Name == catName);
            if (cat == null)
            {
                cat = new ArticleCategory { Name = catName, Articles = 1 };
                db.ArticleCategories.Add(cat);
                db.SaveChanges();
            }
            else
            {
                cat.Articles++;
                db.Entry(cat).State = EntityState.Modified;
            }
            var article = new Article();
            article.CatagoryID = cat.ID;
            article.Title = title;
            article.Content = content;
            article.SourceUrl = srcUrl;
            article.CreateTime = DateTime.Now;
            db.Articles.Add(article);
            db.SaveChanges();
            return this.JsonEx(200, "OK");
        }

        // POST: Articles/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Article article = db.Articles.Find(id);
            db.Articles.Remove(article);
            db.SaveChanges();
            var ids = db.ArticleSimilarities.Where(x => x.LAID == id || x.RAID == id).Select(x => x.ID).ToList();
            db.DeleteByIdList("ArticleSimilarities", ids);
            if (Request.IsAjaxRequest())
            {
                return this.JsonOk();
            }
            return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}
